IF OBJECT_ID('tempdb..#Trans') IS NOT NULL
DROP TABLE #Trans

CREATE TABLE #Trans 
    (ID INT IDENTITY(10001,1), Amount MONEY, [Sign] CHAR(1))

-- Build a Test Harness
;WITH Tally (n) AS (
    SELECT TOP (500) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
    FROM sys.all_columns)
INSERT INTO #Trans (Amount)
SELECT ABS(CHECKSUM(NEWID())) % 80 + 8
FROM Tally

DECLARE @Delimiter CHAR(1) = ','
    ,@ActualTotal MONEY = (SELECT SUM(Amount)FROM #Trans)
    ,@StartDT DATETIME

DECLARE @ReferenceTotal MONEY = ROUND(@ActualTotal * .998, 0)
DECLARE @AmountOfInterest MONEY = @ActualTotal - @ReferenceTotal
DECLARE @n1 INT, @Tuple VARCHAR(8000), @Amount MONEY

PRINT '---- rCTE'
SELECT @StartDT = GETDATE()
SET STATISTICS TIME ON
-- Problem #1: Identify potentially double counted transactions
-- Solution #1a: Use UNIQUEnTuples to enumerate the combinatons and
--               keep only those that qualify
;WITH UNIQUEnTuples (n, Tuples, ID, Amount) AS (
    SELECT 1, CAST(ID AS VARCHAR(8000)), ID, Amount
    FROM #Trans
    WHERE Amount <= @AmountOfInterest
    UNION ALL
    SELECT 1 + n.n, CAST(t.ID AS VARCHAR(8000)) + @Delimiter + n.Tuples
        ,t.ID, t.Amount + n.Amount
    FROM UNIQUEnTuples n 
    CROSS APPLY (SELECT t.ID, Amount FROM #Trans t WHERE t.ID < n.ID AND
        t.Amount + n.Amount <= @AmountOfInterest) t
    WHERE n < 5
    )
SELECT @n1=n, @Tuple=Tuples, @Amount=Amount
FROM UNIQUEnTuples
WHERE Amount = @AmountOfInterest
SET STATISTICS TIME OFF
SELECT StartDT=@StartDT, EndDT=GETDATE(), ElapsedMS=DATEDIFF(millisecond, @StartDT, GETDATE())

PRINT '---- INNER JOINs w-UNION ALL'
SELECT @StartDT = GETDATE()
SET STATISTICS TIME ON
-- Problem #1: Identify potentially double counted transactions
-- Solution #1b: Alternate solution for double counting adhoc analysis
--               using n UNIONed SELECTs, one for each Tuple
SELECT @n1=n, @Tuple=Tuple, @Amount=Amount
FROM (
SELECT n=1, Tuple=CAST(ID AS VARCHAR(8000))
    ,Amount=Amount
FROM #Trans
WHERE Amount = @AmountOfInterest
UNION ALL
SELECT 2
    ,CAST(a.ID AS VARCHAR(8000)) + ',' + CAST(b.ID AS VARCHAR(8000))
    ,a.Amount + b.Amount 
FROM #Trans a
INNER JOIN #Trans b ON a.ID < b.ID AND a.Amount + b.Amount <= @AmountOfInterest
WHERE a.Amount + b.Amount = @AmountOfInterest
UNION ALL
SELECT 3
    ,CAST(a.ID AS VARCHAR(8000)) + ',' + CAST(b.ID AS VARCHAR(8000)) + ',' + 
        CAST(c.ID AS VARCHAR(8000))
    ,a.Amount + b.Amount + c.Amount
FROM #Trans a
INNER JOIN #Trans b ON a.ID < b.ID AND a.Amount + b.Amount <= @AmountOfInterest
INNER JOIN #Trans c ON b.ID < c.ID AND a.Amount + b.Amount + c.Amount <= @AmountOfInterest
WHERE a.Amount + b.Amount + c.Amount = @AmountOfInterest
UNION ALL
SELECT 4
    ,CAST(a.ID AS VARCHAR(8000)) + ',' + CAST(b.ID AS VARCHAR(8000)) + ',' + 
        CAST(c.ID AS VARCHAR(8000)) + ',' + CAST(d.ID AS VARCHAR(8000))
    ,a.Amount + b.Amount + c.Amount + d.Amount
FROM #Trans a
INNER JOIN #Trans b ON a.ID < b.ID AND a.Amount + b.Amount <= @AmountOfInterest
INNER JOIN #Trans c ON b.ID < c.ID AND a.Amount + b.Amount + c.Amount  <= @AmountOfInterest
INNER JOIN #Trans d ON c.ID < d.ID AND a.Amount + b.Amount + c.Amount + d.Amount <= @AmountOfInterest
WHERE a.Amount + b.Amount + c.Amount + d.Amount = @AmountOfInterest
UNION ALL
SELECT 5
    ,CAST(a.ID AS VARCHAR(8000)) + ',' + CAST(b.ID AS VARCHAR(8000)) + ',' + 
        CAST(c.ID AS VARCHAR(8000)) + ',' + CAST(d.ID AS VARCHAR(8000)) + ',' +
         + CAST(e.ID AS VARCHAR(8000)) 
    ,a.Amount + b.Amount + c.Amount + d.Amount + e.Amount
FROM #Trans a
INNER JOIN #Trans b ON a.ID < b.ID AND a.Amount + b.Amount <= @AmountOfInterest
INNER JOIN #Trans c ON b.ID < c.ID AND a.Amount + b.Amount + c.Amount  <= @AmountOfInterest
INNER JOIN #Trans d ON c.ID < d.ID AND a.Amount + b.Amount + c.Amount + d.Amount <= @AmountOfInterest
INNER JOIN #Trans e ON d.ID < e.ID AND a.Amount + b.Amount + c.Amount + d.Amount + e.Amount <= @AmountOfInterest
WHERE a.Amount + b.Amount + c.Amount + d.Amount + e.Amount = @AmountOfInterest) a
SET STATISTICS TIME OFF
SELECT StartDT=@StartDT, EndDT=GETDATE(), ElapsedMS=DATEDIFF(millisecond, @StartDT, GETDATE())

PRINT '---- CROSS APPLYs w-UNION ALL'
SELECT @StartDT = GETDATE()
SET STATISTICS TIME ON
-- Problem #1: Identify potentially double counted transactions
-- Solution #1c: Alternate solution for double counting adhoc analysis
--               using n UNIONed CROSS APPLYs, one for each Tuple
SELECT @n1=n, @Tuple=Tuple, @Amount=Amount
FROM (
SELECT n=1, Tuple=CAST(ID AS VARCHAR(8000))
    ,Amount=Amount
FROM #Trans
WHERE Amount = @AmountOfInterest
UNION ALL
SELECT 2
    ,CAST(a.ID AS VARCHAR(8000)) + ',' + CAST(b.ID AS VARCHAR(8000))
    ,a.Amount + b.Amount 
FROM #Trans a
CROSS APPLY (
    SELECT ID, Amount FROM #Trans b WHERE a.ID < b.ID AND a.Amount + b.Amount <= @AmountOfInterest) b 
WHERE a.Amount + b.Amount = @AmountOfInterest
UNION ALL
SELECT 3
    ,CAST(a.ID AS VARCHAR(8000)) + ',' + CAST(b.ID AS VARCHAR(8000)) + ',' + 
        CAST(c.ID AS VARCHAR(8000))
    ,a.Amount + b.Amount + c.Amount
FROM #Trans a
CROSS APPLY (
    SELECT ID, Amount FROM #Trans b WHERE a.ID < b.ID AND a.Amount + b.Amount <= @AmountOfInterest) b 
CROSS APPLY (
    SELECT ID, Amount FROM #Trans c WHERE b.ID < c.ID AND a.Amount + b.Amount + c.Amount <= @AmountOfInterest) c 
WHERE a.Amount + b.Amount + c.Amount = @AmountOfInterest
UNION ALL
SELECT 4
    ,CAST(a.ID AS VARCHAR(8000)) + ',' + CAST(b.ID AS VARCHAR(8000)) + ',' + 
        CAST(c.ID AS VARCHAR(8000)) + ',' + CAST(d.ID AS VARCHAR(8000)) 
    ,a.Amount + b.Amount + c.Amount + d.Amount
FROM #Trans a
CROSS APPLY (
    SELECT ID, Amount FROM #Trans b WHERE a.ID < b.ID AND a.Amount + b.Amount <= @AmountOfInterest) b 
CROSS APPLY (
    SELECT ID, Amount FROM #Trans c WHERE b.ID < c.ID AND a.Amount + b.Amount + c.Amount <= @AmountOfInterest) c 
CROSS APPLY (
    SELECT ID, Amount FROM #Trans d WHERE c.ID < d.ID AND a.Amount + b.Amount + c.Amount + d.Amount <= @AmountOfInterest) d 
WHERE a.Amount + b.Amount + c.Amount + d.Amount = @AmountOfInterest
UNION ALL
SELECT 5
    ,CAST(a.ID AS VARCHAR(8000)) + ',' + CAST(b.ID AS VARCHAR(8000)) + ',' + 
        CAST(c.ID AS VARCHAR(8000)) + ',' + CAST(d.ID AS VARCHAR(8000)) + ',' +
        CAST(e.ID AS VARCHAR(8000))
    ,a.Amount + b.Amount + c.Amount + d.Amount + e.Amount
FROM #Trans a
CROSS APPLY (
    SELECT ID, Amount FROM #Trans b WHERE a.ID < b.ID AND a.Amount + b.Amount <= @AmountOfInterest) b 
CROSS APPLY (
    SELECT ID, Amount FROM #Trans c WHERE b.ID < c.ID AND a.Amount + b.Amount + c.Amount <= @AmountOfInterest) c 
CROSS APPLY (
    SELECT ID, Amount FROM #Trans d WHERE c.ID < d.ID AND a.Amount + b.Amount + c.Amount + d.Amount <= @AmountOfInterest) d 
CROSS APPLY (
    SELECT ID, Amount FROM #Trans e WHERE d.ID < e.ID AND a.Amount + b.Amount + c.Amount + d.Amount + e.Amount <= @AmountOfInterest) e 
WHERE a.Amount + b.Amount + c.Amount + d.Amount + e.Amount = @AmountOfInterest) a
SET STATISTICS TIME OFF
SELECT StartDT=@StartDT, EndDT=GETDATE(), ElapsedMS=DATEDIFF(millisecond, @StartDT, GETDATE())

CREATE TABLE #Temp2 (n INT, Tuples VARCHAR(8000), Amount MONEY, ID INT)

PRINT '---- Set-based WHILE loop'
SELECT @StartDT = GETDATE()
SET STATISTICS TIME ON
DECLARE @RowCount INT, @n INT = 1

-- Generate first result set (equivalent to anchor leg) into temp table
INSERT INTO #Temp2
SELECT @n, CAST(ID AS VARCHAR(8000)), Amount, ID
FROM #Trans
WHERE Amount <= @AmountOfInterest
SELECT @RowCount = @@ROWCOUNT

-- WHILE loop simulates recursive leg of the rCTE
WHILE @RowCount <> 0 AND @n < 5
BEGIN
    SELECT @n = @n + 1
    --PRINT 'n=' + CAST(@n AS VARCHAR) 
    INSERT INTO #Temp2
    SELECT @n
        ,CAST(a.ID AS VARCHAR(8000)) + ',' + Tuples
        ,Amount=a.Amount + b.Amount 
        ,a.ID
    FROM #Trans a
    INNER JOIN #Temp2 b ON b.n = @n - 1 AND 
        a.ID < b.ID AND
        a.Amount + b.Amount <= @AmountOfInterest

    SELECT @RowCount = @@ROWCOUNT 
END

-- Finally SELECT out based on the amount of interest
SELECT @n1=n, @Tuple=Tuples, @Amount=Amount
FROM #Temp2
WHERE Amount = @AmountOfInterest
SET STATISTICS TIME OFF
SELECT StartDT=@StartDT, EndDT=GETDATE(), ElapsedMS=DATEDIFF(millisecond, @StartDT, GETDATE())
DROP TABLE #Temp2
GO


DROP TABLE #Trans